export { SlidezCanvas }

// 定义配置选项接口
interface SlidezOptions {
  container: HTMLElement
  imageSrc: string
  bezierCtrls: { a: Vec3; b: Vec3; c: Vec3 }
  maxSplit: number
}

// 定义点坐标类型
type Vec2 = { x: number; y: number }
type Vec3 = { x: number; y: number; z: number }

// 定义三角形数据
type TriangleData = {
  points: Vec2[]
  col: number
  row: number
}

class SlidezCanvas {
  private container: HTMLElement
  private canvas: HTMLCanvasElement
  private ctx: CanvasRenderingContext2D
  private image: HTMLImageElement
  private maxSplit: number
  private bezierCtrls: { a: Vec3; b: Vec3; c: Vec3 }
  private size: { width: number; height: number }
  private grid: { cols: number; rows: number; width: number; height: number }
  private triangles: TriangleData[]
  private isImageLoaded: boolean = false

  constructor(options: SlidezOptions) {
    this.container = options.container
    this.maxSplit = options.maxSplit
    this.bezierCtrls = options.bezierCtrls
    this.triangles = []
    this.size = { width: 0, height: 0 }
    this.grid = { cols: 0, rows: 0, width: 0, height: 0 }

    // 创建Canvas元素
    this.canvas = document.createElement("canvas")
    this.canvas.style.display = "block"
    this.container.appendChild(this.canvas)

    this.ctx = this.canvas.getContext("2d")!
    this.image = new Image()

    this.image.onload = () => {
      const { width, height } = this.image
      this.size = { width, height }
      this.grid = getGrid(this.maxSplit, { width, height })

      // 设置canvas尺寸
      this.canvas.width = width
      this.canvas.height = height
      this.canvas.style.width = width + "px"
      this.canvas.style.height = height + "px"

      this.isImageLoaded = true
      this.generate()
      this.render()
    }

    this.image.onerror = () => {
      console.error("图片加载失败:")
    }

    this.image.src = options.imageSrc
  }

  /** 生成三角形数据 */
  generate(): void {
    this.triangles = []
    const { cols, rows } = this.grid

    for (let row = 0; row < rows; row++) {
      for (let col = 0; col < cols; col++) {
        this.createTrianglePair(col, row)
      }
    }
  }

  private createTrianglePair(col: number, row: number): void {
    const { width, height } = this.grid
    const x: number = col * width
    const y: number = row * height

    // 三角形1: 左上角
    const triangle1: TriangleData = {
      points: [
        { x, y },
        { x: x + width, y },
        { x, y: y + height },
      ],
      col,
      row,
    }

    // 三角形2: 右下角
    const triangle2: TriangleData = {
      points: [
        { x: x + width, y },
        { x: x + width, y: y + height },
        { x, y: y + height },
      ],
      col,
      row,
    }

    this.triangles.push(triangle1, triangle2)
  }

  private render(): void {
    if (!this.isImageLoaded) return

    // 清空画布
    this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height)

    // 绘制所有三角形
    this.triangles.forEach((triangle) => {
      this.drawTriangle(triangle)
    })
  }

  private drawTriangle(triangle: TriangleData): void {
    const { points } = triangle

    this.ctx.save()

    // 创建三角形路径
    this.ctx.beginPath()
    this.ctx.moveTo(points[0].x, points[0].y)
    this.ctx.lineTo(points[1].x, points[1].y)
    this.ctx.lineTo(points[2].x, points[2].y)
    this.ctx.closePath()

    // 设置裁剪区域
    this.ctx.clip()

    // 绘制图像
    this.ctx.drawImage(this.image, 0, 0, this.size.width, this.size.height)

    this.ctx.restore()
  }

  private drawTransformedTriangle(
    triangle: TriangleData,
    transform: {
      translate: Vec3
      scale: number
      skew: number
      opacity: number
    }
  ): void {
    const { points } = triangle
    const { translate, scale, skew, opacity } = transform

    this.ctx.save()

    // 设置透明度
    this.ctx.globalAlpha = opacity

    // 计算三角形中心点
    const centerX = (points[0].x + points[1].x + points[2].x) / 3
    const centerY = (points[0].y + points[1].y + points[2].y) / 3

    // 应用变换
    this.ctx.translate(centerX + translate.x, centerY + translate.y)
    this.ctx.scale(1, scale)
    this.ctx.transform(1, 0, Math.tan((skew * Math.PI) / 180), 1, 0, 0)
    this.ctx.translate(-centerX, -centerY)

    // 创建三角形路径
    this.ctx.beginPath()
    this.ctx.moveTo(points[0].x, points[0].y)
    this.ctx.lineTo(points[1].x, points[1].y)
    this.ctx.lineTo(points[2].x, points[2].y)
    this.ctx.closePath()

    // 设置裁剪区域
    this.ctx.clip()

    // 绘制图像
    this.ctx.drawImage(this.image, 0, 0, this.size.width, this.size.height)

    this.ctx.restore()
  }

  // 设置滑动效果的动画进度 (0-100)
  public setProgress(progress: number): void {
    this.updateTriangles(progress)
  }

  private updateTriangles(progress: number): void {
    if (!this.isImageLoaded) return

    // 清空画布
    this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height)

    const slideEachRatio = 0.3
    const slideEachProgress = 100 * slideEachRatio
    const maxSplit = Math.max(this.grid.cols, this.grid.rows)
    const slideEachDelay = (100 - slideEachProgress) / maxSplit

    this.triangles.forEach((triangle: TriangleData) => {
      const index = Math.round((triangle.col + triangle.row) / 2)
      let thisProgress = (progress - slideEachDelay * index) / 100

      if (thisProgress > 0) {
        const pos = getCubicBezierPos(thisProgress, this.bezierCtrls)
        const ratio = 1 - thisProgress

        this.drawTransformedTriangle(triangle, {
          translate: pos,
          scale: ratio,
          skew: thisProgress * 180,
          opacity: ratio,
        })
      } else {
        this.drawTriangle(triangle)
      }
    })
  }
}

function getGrid(maxSplit: number, size: { width: number; height: number }) {
  const gridSize = Math.max(size.width, size.height) / maxSplit
  const cols = Math.ceil(size.width / gridSize)
  const rows = Math.ceil(size.height / gridSize)
  return {
    cols,
    rows,
    width: size.width / cols,
    height: size.height / rows,
  }
}

// 三次贝塞尔曲线优化版本 - 使用向量运算，按相对位置默认起点为{0,0}
function getCubicBezierPos(ratio: number, ctrls: { a: Vec3; b: Vec3; c: Vec3 }): Vec3 {
  const r = 1.0 - ratio
  const r2 = r * r
  const t2 = ratio * ratio
  const b1 = 3.0 * r2 * ratio
  const b2 = 3.0 * r * t2
  const b3 = t2 * ratio

  return {
    x: b1 * ctrls.a.x + b2 * ctrls.b.x + b3 * ctrls.c.x,
    y: b1 * ctrls.a.y + b2 * ctrls.b.y + b3 * ctrls.c.y,
    z: b1 * ctrls.a.z + b2 * ctrls.b.z + b3 * ctrls.c.z,
  }
}
